// Copyright (C) 2010 Félix Cloutier.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

#include "JitRecompiler.h"
#include "JitExecutor.h"
#include "Coroutine.h"
#include <llvm/Support/TypeBuilder.h>

#define INST(name) \
	void JitRecompiler::name(UGeckoInstruction inst, JitFunctionContext& functionContext)

namespace LLVMJit
{

INST(bx)
{
	llvm::IRBuilder<>& builder = functionContext.GetBuilder();
	
	u32 target = SignExt26(inst.LI << 2);
	if (!inst.AA)
		target += functionContext.GetPC();
	
	if (inst.LK) // this is a call
	{
		StoreLink(builder, builder.getInt32(functionContext.GetPC() + 4));
		llvm::Function* functionToCall = GetFunction(target);
		llvm::Value* callResult = builder.CreateCall3(
			functionToCall,
			functionContext.GetJitExecutor(),
			functionContext.GetCoroutine(),
			functionContext.GetPowerPCState());
		functionContext.AddCycles(callResult);
	}
	else
	{
		functionContext.PushBranch(target);
		builder.CreateBr(functionContext.GetBlock(target));
	}
	
	// the PPC block is well-terminated, so we don't need to set a new insert
	//	point on the builder
}

INST(bcx)
{
	llvm::IRBuilder<>& builder = functionContext.GetBuilder();
	
	// BO? CTR? Will have to read on that
	if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
	{
		llvm::Value* ctr = LoadCTR(builder);
		llvm::Value* decrementedCtr = builder.CreateSub(ctr, builder.getInt8(1));
		StoreCTR(builder, decrementedCtr);
	}
	
	const bool checkCounterOnly = (inst.BO >> 4) & 1;
	const bool checkConditionOnly = (inst.BO >> 2) & 1;
	const bool secondBOBit = (inst.BO >> 1) & 1;
	const bool rightValue = ((inst.BO >> 3) & 1); //GetCRBit(inst.BI) == ;
	
	const llvm::Type* i1 = llvm::TypeBuilder<llvm::types::i<8>, false>::get(builder.getContext());
	
	llvm::Value* ctr = LoadCTR(builder);
	llvm::Value* llvmRightValue = builder.getInt1(rightValue);
	llvm::Value* llvmCheckCounterOnly = builder.getInt1(checkCounterOnly);
	llvm::Value* llvmCheckConditionOnly = builder.getInt1(checkConditionOnly);
	llvm::Value* llvmSecondBOBit = builder.getInt1(secondBOBit);
	llvm::Value* ctrNonZero = builder.CreateICmpNE(ctr, builder.getInt8(0));
	llvm::Value* ctrCheck = builder.CreateXor(ctrNonZero, llvmSecondBOBit);
	llvm::Value* crValue = LoadCR(builder, inst.BI);
	llvm::Value* crBit = builder.CreateTrunc(crValue, i1);
	llvm::Value* crBitIsRightValue = builder.CreateICmpEQ(llvmRightValue, crBit);
	
	llvm::Value* counterCondition = builder.CreateOr(llvmCheckConditionOnly, ctrCheck);
	llvm::Value* condition = builder.CreateOr(llvmCheckCounterOnly, crBitIsRightValue);
	
	llvm::Value* branchCondition = builder.CreateAnd(counterCondition, condition);
	
	llvm::BasicBlock* ifTrue;
	llvm::BasicBlock* ifFalse = functionContext.MakeConditionalBlock(false);
	
	u32 target = SignExt16(inst.BD << 2);
	if (inst.AA)
		target += functionContext.GetPC();
	
	if (inst.LK) // conditional call
	{
		llvm::BasicBlock* previousBlock = builder.GetInsertBlock();
		ifTrue = functionContext.MakeConditionalBlock(true);
		builder.SetInsertPoint(ifTrue);
		StoreLink(builder, builder.getInt32(functionContext.GetPC() + sizeof inst));
		llvm::Function* functionToCall = GetFunction(target);
		llvm::Value* callResult = builder.CreateCall3(
			functionToCall,
			functionContext.GetJitExecutor(),
			functionContext.GetCoroutine(),
			functionContext.GetPowerPCState());
		functionContext.AddCycles(callResult);
		builder.CreateBr(ifFalse);
		builder.SetInsertPoint(previousBlock);
	}
	else
	{
		functionContext.PushBranch(target);
		ifTrue = functionContext.GetBlock(target);
	}
	
	builder.CreateCondBr(branchCondition, ifTrue, ifFalse);
	// the PPC block isn't fully compiled, so we have to set a new insert point
	//	to complete it
	builder.SetInsertPoint(ifFalse);
}

INST(bcctrx)
{
	llvm::IRBuilder<>& builder = functionContext.GetBuilder();
	if ((inst.BO_2 & BO_DONT_DECREMENT_FLAG) == BO_DONT_DECREMENT_FLAG)
	{
		PanicAlert("bcctrx with decrement and test CTR option is invalid!");
	}
	
	const llvm::Type* i1 = builder.getInt1Ty();
	
	// Couldn't decypher any kind of meaning in the bcctrx interpreter function
	//	so this is brainless retranscription. I have no clue what it really
	//	does.
	const bool a = (inst.BO_2 >> 3) & 1;
	const bool y = (inst.BO_2 >> 4) & 1;
	
	llvm::Value* crBit = LoadCR(builder, inst.BI_2);
	llvm::Value* crBitTruncated = builder.CreateTrunc(crBit, i1);
	llvm::Value* x = builder.CreateICmpEQ(crBitTruncated, builder.getInt1(a));
	llvm::Value* z = builder.CreateOr(x, builder.getInt1(y));
	
	llvm::BasicBlock* ifTrue = functionContext.MakeConditionalBlock(true);
	llvm::BasicBlock* ifFalse = functionContext.MakeConditionalBlock(false);
	
	builder.CreateCondBr(z, ifTrue, ifFalse);
	builder.SetInsertPoint(ifTrue);
	
	llvm::Value* ctr = LoadCTR(builder);
	llvm::Value* address = builder.CreateAnd(ctr, builder.getInt32(~3));
	// This is an indirect call; we can't figure the address at recompile time.
	// We need to call RunStub, but we also have to be careful that we set it as
	//	a tail call, otherwise we risk the stack overflow.
	llvm::Function* runStub = bindings->GetBinding(JitBindings::RunStub);
	llvm::Value* executor = functionContext.GetJitExecutor();
	llvm::Value* coroutine = functionContext.GetCoroutine();
	llvm::CallInst* runStubCall = builder.CreateCall3(runStub, executor, coroutine, address);
	runStubCall->setTailCall();
	builder.CreateBr(ifFalse);
	
	builder.SetInsertPoint(ifFalse);
}

INST(bclrx)
{
	llvm::IRBuilder<>& builder = functionContext.GetBuilder();
	llvm::Value* ctr = LoadCTR(builder);
	if ((inst.BO_2 & BO_DONT_DECREMENT_FLAG) == 0)
	{
		llvm::Value* minusOne = builder.CreateSub(ctr, builder.getInt8(1));
		ctr = StoreCTR(builder, ctr);
	}
	
	const u8 shiftedBo = inst.BO_2 >> 1;
	const u8 doubleShiftedBo = inst.BO_2 >> 2;
	
	llvm::Value* ctrIsNotZero = builder.CreateICmpNE(ctr, builder.getInt8(0));
	llvm::Value* extended = builder.CreateZExt(ctrIsNotZero, builder.getInt8Ty());
	llvm::Value* xored = builder.CreateXor(ctrIsNotZero, builder.getInt1(shiftedBo & 1));
	llvm::Value* ored = builder.CreateOr(xored, builder.getInt8(doubleShiftedBo));
	llvm::Value* counter = builder.CreateTrunc(ored, builder.getInt1Ty());
	
	const u8 quadrupleShiftedBo = (inst.BO_2 >> 4) & 1;
	const u8 tripleShiftedBo = (inst.BO_2 >> 3) & 1;
	llvm::Value* crBit = LoadCR(builder, inst.BI_2);
	llvm::Value* crBitIsEqual = builder.CreateICmpEQ(crBit, builder.getInt1(tripleShiftedBo));
	llvm::Value* condition = builder.CreateOr(crBitIsEqual, builder.getInt1(quadrupleShiftedBo));
	
	llvm::BasicBlock* ifTrue = functionContext.MakeConditionalBlock(true);
	llvm::BasicBlock* ifFalse = functionContext.MakeConditionalBlock(false);
	
	// look if it's time to yield back to the emulator
	// and if so, do it before returning
	llvm::Function* yieldMaybe = bindings->GetBinding(JitBindings::YieldMaybe);
	builder.CreateCall2(yieldMaybe, functionContext.GetCoroutine(), functionContext.GetCycles());
	
	// goddamn, who's the genius who thought "return" could be a call?
	if (inst.LK_3)
	{
		builder.SetInsertPoint(ifTrue);
		llvm::Value* lr = LoadLink(builder);
		StoreLink(builder, builder.getInt32(functionContext.GetPC() + sizeof inst));
		llvm::Value* address = builder.CreateAnd(lr, builder.getInt32(~3));
		llvm::Function* runStub = bindings->GetBinding(JitBindings::RunStub);
		llvm::Value* executor = functionContext.GetJitExecutor();
		llvm::Value* coroutine = functionContext.GetCoroutine();
		llvm::CallInst* runStubCall = builder.CreateCall3(runStub, executor, coroutine, address);
		runStubCall->setTailCall();
		builder.CreateBr(ifFalse);
	}
	else
	{
#ifndef _MSC_VER
#warning We currently have no way to detect if the link register was messed with
#endif
		builder.CreateRet(functionContext.GetCycles());
	}
	
	builder.SetInsertPoint(ifFalse);
}

}